package com.hmdp.utils;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.json.JSONUtil;
import com.hmdp.entity.Shop;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;

import java.lang.reflect.InvocationTargetException;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;

/**
 * @author 衷岚
 * @version 1.0
 */
@Component
@Slf4j
public class CacheClient {

    @Autowired
    private RedisTemplate redisTemplate;

    /**
     * 将任意java对象序列化并缓存为hush类型，
     *
     * 可以设置ttl过期时间
     * @param key
     * @param value
     * @param time
     * @param timeUnit
     */
    public void set(String key, Object value, Long time, TimeUnit timeUnit) {
        redisTemplate.opsForHash().putAll(key,BeanUtil.beanToMap(value));
        redisTemplate.expire(key,time,timeUnit);
    }

    /**
     *      * 将任意java对象序列化并缓存为hush类型，
     *      *
     *      * 可以设置逻辑过期时间
     * @param key
     * @param value
     * @param time
     * @param timeUnit
     */
    public void setWithLogicalExp(String key, Object value, Long time, TimeUnit timeUnit) {

        //封装成逻辑过期数据
        RedisData data = new RedisData();
        data.setData(value);
        data.setExpireTime(LocalDateTime.now().plusSeconds(timeUnit.toSeconds(time)));
        redisTemplate.opsForHash().putAll(key,BeanUtil.beanToMap(data));
    }


    /**
     *
     * @param keyPrefix  key缓存key前缀
     * @param id         id
     * @param type      返回值类信息
     * @param dbFallback 查库方法
     * @param time       写入redis时间
     * @param timeUnit   写入redis时间单位
     * @return              返回对应返回值类型的实例
     * @param <R>       返回值类型
     * @param <ID>      id类型
     */
    public <R,ID> R findWithPassThrough(String keyPrefix, ID id, Class<R> type, Function<ID,R> dbFallback, Long time, TimeUnit timeUnit) {

        //查询redis
        String cacheKey = keyPrefix + id;
        Map cacheMap = redisTemplate.opsForHash().entries(cacheKey);

        //判断是否是空值
        if (cacheMap.containsKey("_empty")) {
            return null;
        }
        //命中直接返回
        if (!cacheMap.isEmpty()) {
            R result = null;
            try {
                //创建实例
                result = type.getDeclaredConstructor().newInstance();
                //转换成java对象
                BeanUtil.fillBeanWithMap(cacheMap,result, false);
                //返回
                return result;
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }



        //没有命中
        //查数据库
        R dbResult = dbFallback.apply(id);

        //防止穿透
        if (dbResult == null) {
            log.info("不存在");
            //参数为空map不会写入缓存！！！

            //此处设置占位字段标记
            Map<String, Object> map = new HashMap<>();
            map.put("_empty", true);
            redisTemplate.opsForHash().putAll(cacheKey, map);
            redisTemplate.expire(cacheKey, RedisConstants.CACHE_NULL_TTL, TimeUnit.MINUTES);
            return null;
        }

        //写入缓存
        set(cacheKey,dbResult,time,timeUnit);

        return dbResult;
    }
}
